// 和后台关联的部分工具

import s94Mjs from "s94-js";
import {_href, _url_api, cache_local} from "@/js/s94_tool.js";
import {href_handle} from "@/vue_plugin/router_auto.js";
import Spark_md5 from "spark-md5";
import wx from '@/js/jweixin-1.6.0.js';
import tool from "@/js/tool.js";
import {loading, msg} from "@/js/vant_tool.js";
import {use_config_map} from "@/store/config_map.js";
import {use_this_admin} from "@/store/this_admin.js";
import {use_this_user} from "@/store/this_user.js";

export {authorization, ajax, file_upload, pay_handle, wechat_sdk, wechat_share, page_card}

let authorization = {
    authorizationMap: undefined,
    cacheKey: 'authorizationMap',
    init(){
        if (!this.authorizationMap){
            this.authorizationMap = cache_local(this.cacheKey, {})
        }
    },
    urlToHost(url){
        return url.replace(/^(?:.*\/\/)?([\w.]+).*$/, '$1');
    },
    get(url) {
        this.init();
        return this.authorizationMap[this.urlToHost(url)];
    },
    set(url, authorization) {
        this.init();
        let host = this.urlToHost(url);
        if (this.authorizationMap[host] === authorization) return;
        this.authorizationMap[host] = authorization;
        cache_local({[this.cacheKey]: this.authorizationMap});
    }
}

/**统一的http请求封装
 * @param {Object|String} op 配置参数，如果传入字符串，那么看作是url
 * @param {String} op.url 地址
 * @param {String} [op.method] 请求方法，不区分大小写，默认：GET
 * @param {Object|Array} [op.headers] 头信息
 * @param {Number} [op.timeout] 过期时长(毫秒)，默认：30000
 * @param {Boolean} [op.loading] 显示加载中弹窗，默认：false
 * @param {Boolean} [op.async] 异步，默认：true
 * @param {Boolean} [op.cache] 是否启用缓存，存在缓存数据使用缓存数据触发success，然后在异步请求数据，根据真实响应结果和refresh判断是否还需要重复触发success，默认：false
 * @param {Boolean} [op.refresh] 响应数据和缓存数据不同是否需要触发success，默认：true
 * @param {Number} [op.showMessage] 显示消息，建议使用常量ajax.SHOW_MESSAGE_*，默认：ajax.SHOW_MESSAGE_FAIL
 * @param {Function} [ok] 请求成功回调，回调函数的优势：1、可以使用this，2、返回false可以阻止默认的消息提示，3、能处理缓存模式下重复触发的success
 * @param {Function} [fail] 请求失败回调，回调函数的优势：1、可以使用this，2、返回false可以阻止默认的消息提示
 * @returns {Promise<{message:string, data?:*}>}
 */
function ajax(op, ok, fail){
    //默认配置
    if(typeof(op)==='string') op = {url: op};
    //请求接口处理，在请求url的前面加上代理服务器地址，然后，这样如果设定了
    if (import.meta.env.VITE_PROXY_API_HOST){
        //如果使用了代理服务，请求接口加上代理前缀
        op.url = '/proxy_api'+op.url;
    }else {
        op.url = _url_api(op.url);
    }
    op.headers = op.headers || {};
    op.headers['content-type'] = "application/json";
    op.headers['authorization'] = authorization.get(op.url) || '';
    let needLoading = op.loading;
    if(needLoading) {
        ajax.loadingCount++;
        loading.show();
    }
    let showMessageCode = parseInt(op.showMessage);
    if (isNaN(showMessageCode)) showMessageCode = ajax.SHOW_MESSAGE_FAIL;

    return (new Promise((resolve, reject) => {
        ok = typeof ok === 'function' ? ok : function (){}
        fail = typeof fail === 'function' ? fail : function (){}

        op.success = function(responseData){
            // 更新授权数据
            if (this.response.headers.authorization){
                authorization.set(this.url, this.response.headers.authorization)
            }
            let showMessage = showMessageCode & ajax.SHOW_MESSAGE_OK;
            if(ok.call(this, responseData)!==false && responseData.message && showMessage) msg.ok(responseData.message);
            resolve(responseData);
        }
        op.error = function(responseData){
            this.cleanCache();
            if (typeof responseData == 'string') msg.fail('网络异常，请重试：'+this.response.code);
            if (typeof(ajax.errorCode[this.response.code])=='function' && !this.skip_error_handle) {
                responseData = ajax.errorCode[this.response.code].call(this, responseData);
            }
            let showMessage = showMessageCode & ajax.SHOW_MESSAGE_FAIL;
            if(fail.call(this, responseData)!==false && responseData.message && showMessage) msg.fail(responseData.message);
            reject(ajax.responseError(this.response));
        }
        op.complete = function (response){
            // complete 在 success、error 回调执行后执行
            if(needLoading) ajax.loadingCount--;
            if (ajax.loadingCount <= 0) loading.hide();
        }
        return s94Mjs.ajax(op);
    }))
}
ajax.responseError = (response)=>{
    let message = typeof response.data === 'string' ? `响应数据异常：${response.code}` : (response.data?.message || '未知错误');
    let err = new Error(message);
    err.response = response;
    return err;
}
ajax.SHOW_MESSAGE_NONE = 0;
ajax.SHOW_MESSAGE_FAIL = 1;
ajax.SHOW_MESSAGE_OK = 2;
ajax.SHOW_MESSAGE_ALL = 3;
ajax.loadingCount = 0;
/**ajax请求错误码对应处理
 * @param 401 权限验证异常
 * @param 422 数据验证异常
 */
ajax.errorCode = {
    '401': function (res){ //未登录的处理
        let path = '/user/login';
        if (/\/admin\//.test(this.url)){
            path = '/admin/login';
            use_this_admin().$reset()
        }else {
            path = use_config_map().config.page?.link?.user_login_url;
            path = path ? path : '/user/login';
            // 记录一下来源地址，免得微信授权的时候，回退到授权接口
            s94Mjs.cache({'user_login_referer': location.href});
            use_this_user().$reset()
        }
        href_handle(path, 1500);
        return res;
    },
    '402': function (res){ //权限不足的处理
        if (this.skip_402) return res;
        let path = '/index';
        href_handle(path, 1500);
        return res;
    },
    '405': function (res){ //租户过期的处理
        let path = '/expire';
        href_handle(path, 1500);
        return res;
    },
    '422': function (res){ //参数验证错误的处理
        let errors = [];
        s94Mjs.eachloop(res.errors,'', function ($row){
            errors.push($row);
        })
        res.message = errors.join(' | ');
        return res;
    },
}

/**请求缓存，使用 sign-cache 机制
 * @param {Object|String} op 配置参数，如果传入字符串，那么看作是url
 * @param {String} op.url 地址
 * @param {Object|Array} [op.headers] 头信息
 * @param {Number} [op.timeout] 过期时长(毫秒)，默认：30000
 * @param {Boolean} [op.async] 异步，默认：true
 * @param {Number} [op.showMessage] 显示消息，建议使用常量ajax.SHOW_MESSAGE_*，默认：ajax.SHOW_MESSAGE_ALL
 * @returns {Promise<{message:string, data?:*}>}
 */
export function requestCache(op){
    if (typeof op === 'string') op = {url: op}
    let [path, search] = op.url.split('?');
    // 获取排序后的参数键值对后来确定 cacheKey
    let cacheKey = 'CacheSign:' + path + '?' + Array.from((new URLSearchParams(search)).entries())
        .sort((a, b) => a[0].localeCompare(b[0]))
        .map(([key, value])=>`${key}=${value}`).join('&');

    // 获取缓存的数据和签名
    let cacheData = cache_local(cacheKey, {});
    let {res:cacheRes, sign} = cacheData;
    op.url += `${(~op.url.indexOf('?') ? '&' : '?')}_cache_sign_=${sign||'-'}`;

    return ajax(op, function(res){
        if (res){
            // 获取sign，然后缓存数据
            sign = this.response.headers['cache-sign'];
            cache_local({[cacheKey]: {res,sign}});
        }else {
            console.log(cacheRes, this.url);
        }
    }).then(res=>{
        return res || cacheRes;
    })
}


/** 上传文件，请求会发送：md5，name，filesize，file_category_id，file，start_index以及extend_post的参数
 * @param {File} file 文件对象
 * @param {Function} [progress] 进度的回调函数，接受一个表示进度的对象(type、progress、message)
 * @param {Object} [extend_post] 额外发送的数据，会覆盖默认值的参数名：name、file_category_id
 * @returns {Promise<unknown>} 返回文件对象
 */
function file_upload(file, progress, extend_post){
    let unit_size = 2*1024*1024;// 单次上传最多size，超过的会执行断点续传
    progress = typeof progress=='function' ? progress : function (){}
    let post_data = {
        md5: '',
        name: file.name,
        filesize: file.size,
        file_category_id: 0,
    }
    if (extend_post && typeof extend_post === 'object'){
        post_data = Object.assign(post_data, extend_post);
    }
    post_data.filesize = file.size;

    /**计算文件MD5
     * @param {File} file 文件对象
     * @param {Function} callback 进度回调函数，接受一个参数表示解析进度(0-1)
     * @returns {Promise<unknown>} 传递计算后的MD5
     */
    function file_md5(file, callback){ //得到文件MD5，next传入md5字符串
        var md5 = new Spark_md5();
        let unit_size = 10*1024*1024;
        let fr = new FileReader();

        let over = false, start = 0;
        function loop(){
            let end = 0;
            if(start + unit_size > file.size){
                end = file.size;
                over = true;
            }else {
                end = start + unit_size;
            }
            let data = file.slice(start, end);
            fr.readAsBinaryString(data);
            start = end;
        }
        return new Promise(function (next){
            fr.onload = function (e){
                md5.appendBinary(this.result);
                callback(start/file.size);
                if (over){
                    next(md5.end());
                }else {
                    loop();
                }
            }
            loop();
        })
    }

    /**分片上传文件
     * @param {File} file 上传的文件
     * @param {Number} start 开始上传的未知
     * @param {Function} callback 每次上传的回调函数，接受2个参数(请求响应数据, 上传进度)
     */
    function upload_unit(file, start, callback){
        start = Number(start);
        let end = Math.min(start + unit_size, file.size);
        let over = end >= file.size;
        var fd = new FormData();
        var unit_f = (start==0&&over) ? file : new File([file.slice(start, end)], file.name, {type: file.type});
        fd.append('file', unit_f);
        fd.append('start_index', start);
        for (const k in post_data) {
            fd.append(k, post_data[k]);
        }
        ajax({url:'/home/api/file_upload', data: fd, method:'POST'}, function (res){
            let data = res.data;
            if (data.real_size >= data.size){
                callback(res, {type:'upload', progress:1, message:'上传成功'});
            }else {
                callback(res, {type:'upload', progress:data.real_size/data.size, message:'上传中'});
                setTimeout(function (){
                    upload_unit(file, data.real_size, callback);
                },5);
            }
            return false;
        }, function (res){
            callback(res, {type:'upload_error', progress:1, message:'上传文件出错'});
            return false;
        })
    }

    //第一步，计算md5
    return file_md5(file, function (p){
        progress({type:'md5', progress:p, message:'解析中'});
    }).then(function (md5){
        post_data.md5 = md5;
        return new Promise(function (over, reject){
            upload_unit(file, 0, function (res, p){
                if (p.type == 'upload_error'){
                    reject(new Error(res.message || p.message));
                }else {
                    progress(p);
                    if (p.progress >= 1) over(res.data);
                }
            })
        })
    });
}

function wechat_pay(config, ok, fail){
    ok = typeof(ok)=="function"?ok:function(){};
    fail = typeof(fail)=="function"?fail:function(){};
    s94Mjs(function (next){
        if (typeof WeixinJSBridge == "undefined") {
            if (document.addEventListener) {
                document.addEventListener('WeixinJSBridgeReady', next, false);
            } else if (document.attachEvent) {
                document.attachEvent('WeixinJSBridgeReady', next);
                document.attachEvent('onWeixinJSBridgeReady', next);
            }
        } else {
            next();
        }
    }).then(function (next){
        WeixinJSBridge.invoke('getBrandWCPayRequest', config, function(res) {
            if (res.err_msg == "get_brand_wcpay_request:ok"){
                if(ok.call(this, res)!==false) msg.ok('支付成功');
            }else {
                if(fail.call(this, res)!==false) msg.fail('已取消');
            }
        });
    })
}

/**全局适用的支付处理，
 * @param {String|Object} op 支付请求的地址
 * @param {Object} [data] 支付请求发送的数据
 * @param {Function} [ok] 支付执行完成后的回调
 * @param {Function} [fail] 支付失败的回调
 */
function pay_handle(op, data, ok, fail){
    let app = this;
    if (typeof op === 'string') op = {url: op};
    ok = typeof(ok)=="function"?ok:function(){};
    fail = typeof(fail)=="function"?fail:function(){};
    data = data || {};

    function data_get(key){
        if (data instanceof FormData){
            return data.get(key);
        }else {
            return s94Mjs.map(data, key);
        }
    }
    function data_set(k, v){
        if (data instanceof FormData){
            data.set(k, v);
        }else {
            return s94Mjs.map(data, {[k]: v});
        }
    }

    s94Mjs(resolve=>{
        let ban_type = op.ban_type || [];
        let pay_type = data_get('pay_type');
        if (pay_type && !~ban_type.indexOf(pay_type)) return pay_type;
        tool.select_pay_type(ban_type, pay_type).then((pay_type_row)=>{
            resolve(pay_type_row);
        }).catch(fail);
    }).then((pay_type_row)=>{
        data_set('pay_type', pay_type_row.name);

        // 发起支付请求
        ajax({url:op.url, data:data, loading:1, methods:'POST'}, (res)=>{
            let log_pay = res.data;
            if (data_get('pay_type') === 'yue') { //修改用户余额缓存
                let user_money = (use_this_user().money||0) - log_pay.pay_money;
                if (user_money>=0) use_this_user().money = user_money;
            }
            if (log_pay.status == 2){
                if(ok.call(this, log_pay)!==false) msg.ok('支付成功');
                return ;
            }
            //支付后置处理
            if (pay_type_row.handle_type === 'offline'){
                //线下支付
                let pay_config = log_pay.pay_return_row;
                tool.offline_pay(pay_config.id, pay_config.pay_img, pay_config.pay_money).then((res2)=>{
                    if(ok.call(this, log_pay)!==false) msg.ok('提交成功，等待管理员审核');
                }).catch(()=>{
                    if(fail.call(this, log_pay)!==false) msg.fail('已取消');
                })
            }else if (pay_type_row.handle_type === 'wechat_pay'){
                //微信支付
                let pay_config = log_pay.pay_return_row;
                wechat_pay(pay_config, ok, fail);
            }else if (pay_type_row.handle_type === 'link'){
                let href = log_pay.pay_return_row;
                href_handle(href);
            }else{
                let text = log_pay.status_alias || (['已取消','待支付','已支付','支付失败','待审核','支付中'][log_pay.status]) || '未知状态';
                if(ok.call(this, log_pay)!==false) msg.ok(text);
            }
        }, fail)
    }).catch((e)=>{
        if(fail.call(this, e)!==false) msg.fail(e.message);
    })
}
function wechat_sdk(callback){
    if (typeof callback === 'function') return wechat_sdk().then(callback,()=>{});
    return new Promise((resolve, reject)=>{
        if (!(s94Mjs.load.os & s94Mjs.OS_H5_WECHAT)) return;
        if (!wechat_sdk.queue) {
            wechat_sdk.queue = new s94Mjs.Queue();
            wechat_sdk.queue.add(function (){
                return new Promise((next)=>{
                    ajax('/home/api/wechat_sdkconfig?'+s94Mjs.param_encode({referer:window.location.href}), (res)=>{
                        wx.config(res.data);
                        wx.ready(function(){
                            next();
                        });
                        wx.error(function(res){
                            wechat_sdk.error = res.errMsg;
                            msg(wechat_sdk.error);
                            next();
                        });
                        return false;
                    }, (res)=>{
                        wechat_sdk.error = res.message;
                        msg(wechat_sdk.error);
                        next();
                        return false;
                    })
                })
            })
        }
        wechat_sdk.queue.add(function (){
            if (typeof wechat_sdk.error === 'undefined'){
                resolve(wx);
            }else {
                reject(wechat_sdk.error);
            }
        })
    })
}
/**分享数据
 * @param {Object} [data] 分享参数，第一优先级
 * @returns {{head_img:string, title:string, url:string, info:string}}
 */
function _share_data(data){
    console.log('_share_data');
    if (typeof data !== 'object') data = {};
    let share_phone = data.share_phone || use_this_user().phone || '';
    let title = data.title || document.title || use_config_map().this_tenant.title || '';
    let head_img = data.head_img || document.querySelector('link[rel="icon"]')?.href || use_config_map().config.page?.base?.logo || use_config_map().this_tenant.head_img || '';
    let info = data.info || document.querySelector('meta[name="description"]')?.content || use_config_map().this_tenant.title || '';
    let url = data.url || window.location.href;
    if (!/(\?\&)_share_phone_=\d{11}/.test(url) && share_phone){
        url += url.indexOf('?')>0 ? '&' : '?' + `_share_phone_=${share_phone}`;
    }
    head_img = _href(head_img);
    url = _href(url);
    return {title, head_img, info, url};
}
function wechat_share(){
    let data = _share_data();
    wechat_sdk().then((wx)=>{
        wx.updateAppMessageShareData({
            title: data.title, // 分享标题
            desc: data.info, // 分享描述
            link: data.url, // 分享链接，该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl: data.head_img, // 分享图标
            success: function () {
                console.log('微信设置APP消息分享数据成功');
            }
        })
        wx.updateTimelineShareData({
            title: data.title, // 分享标题
            link: data.url, // 分享链接，该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl: data.head_img, // 分享图标
            success: function () {
                console.log('微信设置平台分享数据成功');
            }
        })
    },(err)=>{
        console.log('微信设置消息分享数据失败', err)
    })
}

function page_card(path, callback){
    if (typeof callback === 'function') return page_card(path).then(callback,()=>{});
    page_card.cache_data = page_card.cache_data || {};
    path = path || 'index';
    let type_map = ["type_0", "type_1", "type_2", "type_3", "type_4", "type_5"];
    return new Promise((resolve, reject)=>{
        let card_list = s94Mjs.map(page_card.cache_data, path);
        if (card_list) return resolve(card_list);
        card_list = [];
        ajax(`/home/api/page_card?path=${path}`, (res)=>{
            (res.data || []).forEach((row)=>{
                if (!type_map[row.type] || !row.list) return;
                let config = {};
                try { config = JSON.parse(row.config); } catch (e) {}
                card_list.push({
                    component_name: type_map[row.type],
                    title: row.title || "",
                    config: config,
                    model_name: row.model_name || "",
                    href: row.href || "",
                    list: Array.isArray(row.list) ? row.list : [],
                })
            })
            s94Mjs.map(page_card.cache_data, {[path]: card_list});
            resolve(card_list);
            return false;
        }, (res)=>{reject(res.message)})
    })
}
